/*
 * @Author: 三岁小仙仙 scitctocdtu@qq.com
 * @Date: 2024-10-02 18:10:07
 * @LastEditors: 三岁小仙仙 scitctocdtu@qq.com
 * @LastEditTime: 2024-10-06 18:14:03
 * @FilePath: \demo\src\utility\IndexedDB.ts
 * @Description: https://juejin.cn/post/6995715460159242271#heading-19
 *
 * 数据库     IDBDatabase 对象
 * 对象仓库   IDBObjectStore 对象, 类似于关系型数据库中的表
 * 索引      IDBIndex 对象
 * 事务      IDBTransaction 对象
 * 操作请求   IDBRequest 对象
 * 指针      IDBCursor 对象
 * 主键集合   IDBKeyRange 对象
 *
 */

let db: IDBDatabase

export const init = (
	dbName: string,
	store: IndexedDB.TStoreList,
	version?: number,
	callback?: Function
): Promise<IDBDatabase> => {
	return new Promise((resolve, reject) => {
		const openDB = window.indexedDB.open(dbName, version)

		openDB.onsuccess = (e: Event) => {
			let ev = e.target as IDBOpenDBRequest
			console.log(`数据库【${ev.result.name}】打开成功`)
			resolve(ev.result)
			db = ev.result
		}

		openDB.onerror = (e: Event) => {
			let ev = e.target as IDBOpenDBRequest
			console.error(`数据库【${ev.result.name}】打开失败`)
			reject(ev.result)
		}

		openDB.onupgradeneeded = (e: IDBVersionChangeEvent) => {
			db = (e.target as IDBOpenDBRequest).result
			if (callback) {
				callback(e)
			} else {
				let objectStore = null
				Object.keys(store).forEach(key => {
					const st: IndexedDB.TStore = store[key]
					if (!db.objectStoreNames.contains(st.name)) {
						objectStore = db.createObjectStore(st.name, {
							keyPath: st.keyPath,
							autoIncrement: st.autoIncrement,
						})

						if (st.cursorIndex && st.cursorIndex.length > 0) {
							for (let it of st.cursorIndex) {
								objectStore.createIndex(it.key, it.key, {
									unique: it.unique,
								})
							}
						}
						objectStore.transaction.oncomplete = (e: Event) => {
							console.log(`${st.name}仓库创建成功`)
						}
					}
				})
			}
		}
	})
}

export const add = <T>(storeName: string, data: T) => {
	return new Promise((resolve, reject) => {
		const request = db
			.transaction(storeName, "readwrite")
			.objectStore(storeName)
			.add(data)

		request.onsuccess = (e: Event) => {
			resolve((e.target as IDBOpenDBRequest).result)
		}

		request.onerror = (e: Event) => {
			reject((e.target as IDBOpenDBRequest).result)
		}
	})
}

export const batchAdd = <T>(
	storeName: string,
	data: T[]
): Promise<IDBTransaction> => {
	// if(!(data && data.length>0)){
	// 	return Promise.reject(new Error('no data'))
	// }
	return new Promise((resolve, reject) => {
		const transaction = db.transaction(storeName, "readwrite")
		const store = transaction.objectStore(storeName)
		data.forEach(d => store.put(d))
		transaction.oncomplete = ev => resolve(ev.target as IDBTransaction)
		transaction.onabort = ev => reject((ev.target as IDBTransaction).error)
	})
}

export const query = (storeName: string, keyPath: string) => {
	return new Promise((resolve, reject) => {
		const request = db
			.transaction(storeName, "readonly")
			.objectStore(storeName)
			.get(keyPath)

		request.onsuccess = () => resolve(request.result)
		request.onerror = ev => reject(ev)
	})
}

export const queryAll = (storeName: string) => {
	return new Promise(resolve => {
		const request = db
			.transaction(storeName, "readonly")
			.objectStore(storeName)
			.getAll()
		request.onsuccess = () => resolve(request.result)
	})
}

export const queryAllByIndex = <T>(
	storName: string,
	keyPath: string,
	value: IDBValidKey
): Promise<T[]> => {
	return new Promise<T[]>(resolve => {
		const request = db
			.transaction(storName, "readonly")
			.objectStore(storName)
			.index(keyPath)
			.getAll(value)

		request.onsuccess = (ev: Event) => {
			resolve((ev.target as IDBRequest).result)
		}
	})
}

export const remove = (storeName: string, keyPath: string) => {
	return new Promise((resolve, reject) => {
		const request = db
			.transaction(storeName, "readwrite")
			.objectStore(storeName)
			.delete(keyPath)
		request.onsuccess = () => resolve(request.result)
		request.onerror = () => reject(request.result)
	})
}

export const update = (storeName: string, data: any, keyPath?: string) => {
	return new Promise((resolve, reject) => {
		const request = db
			.transaction(storeName, "readwrite")
			.objectStore(storeName)
			.put(data, keyPath)
		request.onsuccess = () => resolve(request.result)
		request.onerror = () => reject(request.result)
	})
}

export const clearAll = (storeName: string) => {
	return new Promise((resolve, reject) => {
		const request = db
			.transaction(storeName, "readwrite")
			.objectStore(storeName)
			.clear()

		request.onsuccess = (e: Event) => {
			resolve((e.target as IDBOpenDBRequest).result)
		}

		request.onerror = (e: Event) => {
			reject((e.target as IDBOpenDBRequest).result)
		}
	})
}

export const close = () => {
	return new Promise(resolve => {
		try {
			db.close()
			resolve(true)
		} catch (error) {
			resolve(false)
		}
	})
}

export default indexedDB
export { db }
